package cn.tedu.charging.order.service.impl;

import cn.tedu.charging.common.pojo.message.CheckResultMessage;
import cn.tedu.charging.common.pojo.message.DelayCheckMessage;
import cn.tedu.charging.common.pojo.message.ProgressData;
import cn.tedu.charging.common.pojo.message.ProgressMessage;
import cn.tedu.charging.common.pojo.param.ProgressCostParam;
import cn.tedu.charging.common.pojo.vo.ProgressCostVO;
import cn.tedu.charging.common.protocol.JsonResult;
import cn.tedu.charging.common.protocol.WebSocketResult;
import cn.tedu.charging.common.utils.SnowflakeIdGenerator;
import cn.tedu.charging.common.utils.TimeConverterUtil;
import cn.tedu.charging.order.clients.CostClient;
import cn.tedu.charging.order.clients.DeviceClient;
import cn.tedu.charging.order.dao.repository.BillRepository;
import cn.tedu.charging.order.dao.repository.ProcessEsRepository;
import cn.tedu.charging.order.points.WebsocketServerPoint;
import cn.tedu.charging.order.pojo.po.ChargingBillFailPO;
import cn.tedu.charging.order.pojo.po.ChargingBillSuccessPO;
import cn.tedu.charging.order.pojo.po.ChargingProgressEsPO;
import cn.tedu.charging.order.service.ConsumerService;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
@Slf4j
public class ConsumerServiceImpl implements ConsumerService {
    @Autowired
    private BillRepository billRepository;
    @Autowired
    private DeviceClient deviceClient;
    //把能推送消息的WebsocketServerPoint注入
    @Autowired
    private WebsocketServerPoint wsServerPoint;
    //操作es的数据层
    @Autowired
    private ProcessEsRepository processEsRepository;
    @Override
    public void handleCheckResult(CheckResultMessage msg) {
        //1.从消息对象解析result
        Boolean result = msg.getResult();
        //1.1 准备一个即将推出的消息 但是先不封装消息内容
        WebSocketResult<String> userMsg=new WebSocketResult<>();
        userMsg.setState(1);//1 失败或异常,这里表示推送的是字符串消息
        userMsg.setMessage("设备自检反馈的消息");
        //判断是否成功
        if(result){
            log.debug("订单:{},设备枪:{},本次自检结果是成功的",
                    msg.getOrderNo(),msg.getGunId());
            //pre2 检查成功订单是否存在
            long count=billRepository.countSuccess(msg.getOrderNo());
            if (count>0){
                log.debug("订单:{},成功订单已存在,本次自检结果是成功的",msg.getOrderNo());
                return;
            }
            //2.存储成功订单数据
            saveBillSuccess(msg);
            //3组织成功启动订单的消息对象
            String data="您的订单,马上开始充电";
            userMsg.setData(data);
        }else{
            log.debug("订单:{},设备枪:{},本次自检结果是失败的,失败原因:{}",
                    msg.getOrderNo(),msg.getGunId(),msg.getFailDesc());
            //pre4
            long count=billRepository.countFail(msg.getOrderNo());
            if (count>0){
                log.debug("订单:{},失败订单已存在,本次自检结果是失败的",msg.getOrderNo());
                return;
            }
            //4.存储失败的订单数据
            saveBillFail(msg);
            //5.组织失败启动订单的消息对象
            //5.1组织失败消息的时候,尽可能降低用户体验下降度,会给用户发一张优惠券
            //调用营销系统优惠券接口 接口返回的是领取优惠券的url地址(短连接地址)
            String data="您的订单创建失败,送您一张优惠券 https://D39f3D_3/abc";
            userMsg.setData(data);
            //6.修改枪状态 ,调用设备 调用deviceClient实现远程处理
        }
        //7.发送消息给用户
        //根据消费信息中 userId,以及上面组织的完整webSocketResult对象将消息推出
        String message = JSON.toJSONString(userMsg);
        try{
            wsServerPoint.pushMsg(message,msg.getUserId());
        }catch (Exception e){
            //[讨论话题之一]推送失败的处理方法 连接异常消息未达,用户下线
            log.error("发送消息给用户失败,userId:{},消息:{}",msg.getUserId(),message,e);
        }
    }
    @Autowired
    private CostClient costClient;
    @Override
    public void handleChargingProgress(ProgressMessage msg) {
        //1.检查是否满足温度安全条件
        checkTemperature(msg.getTemperature());
        //2.调用计价中心计算价格
        ProgressCostVO cost=calculateCost(msg);
        //3.写入es持久化对象 组织ESPO 需要三方面内容
        saveChargingProgress(msg,cost);
        //4.推送消息给用户 告知充电进度详情
        sendProgress2User(cost,msg);
        //判断本次充电是否充满
        if(msg.getIsFull()){
            log.debug("本次充电已满,充电结束");
            //5.让后端数据 订单结束
            updateSuccess(msg);
        }else{
            log.debug("本次充电未充满,继续充电");
        }
    }

    private void updateSuccess(ProgressMessage msg) {
        //将当前订单能够在结束时更新的数据 执行update 写到success表
        ChargingBillSuccessPO success=new ChargingBillSuccessPO();
        success.setUpdateTime(new Date());
        success.setBillStatus(2);//正常结束
        success.setChargingCapacity(msg.getTotalCapacity().intValue());
        success.setChargingDuration(msg.getTotalTime().intValue());
        success.setChargingEndTime(success.getUpdateTime());
        success.setBillId(msg.getOrderNo());
        billRepository.updateSuccess(success);
    }

    private void sendProgress2User(ProgressCostVO cost, ProgressMessage msg) {
        //1.封装一个即将推送给用户的消息
        WebSocketResult<ProgressData> messageResult=new WebSocketResult<>();
        //1.1协议相关 state=3 充电进度详情 message 充电详情
        messageResult.setState(3);
        messageResult.setMessage("充电进度详情");
        //1.2ProgressData
        ProgressData data=new ProgressData();
        //总度数,总金额,单次度数,单价
        data.setTotalCapacity(msg.getTotalCapacity());
        data.setTotalCost(cost.getTotalCost());
        data.setChargingCapacity(cost.getChargingCapacity());
        data.setOneElectricityCost(cost.getPowerFee());
        //利用工具 对当前充电总时间做计算,得到小时数,分钟数,秒数
        data.setHours(TimeConverterUtil.getHour(msg.getTotalTime()).intValue());
        data.setMinutes(TimeConverterUtil.getMinute(msg.getTotalTime()).intValue());
        data.setSeconds(TimeConverterUtil.getSecond(msg.getTotalTime()).intValue());
        messageResult.setData(data);
        //2.给用户推送消息
        String msgJson="";
        try{
            msgJson=JSON.toJSONString(messageResult);
            wsServerPoint.pushMsg(msgJson,msg.getUserId());
        }catch (Exception e){
            log.error("发送消息给用户失败,userId:{},消息:{}",msg.getUserId(),msgJson,e);
        }
    }
    @Autowired
    private SnowflakeIdGenerator snowflakeIdGenerator;
    private void saveChargingProgress(ProgressMessage msg, ProgressCostVO cost) {
        //1.组织一个ProgressEsPO
        ChargingProgressEsPO progressEsPO=new ChargingProgressEsPO();
        //1.1消息属性拷贝给po
        BeanUtils.copyProperties(msg,progressEsPO);
        //1.2补充 capacity totalCost costVO
        progressEsPO.setChargingCapacity(cost.getChargingCapacity());
        progressEsPO.setTotalCost(cost.getTotalCost());
        //1.3补充一个雪花算法id 目的: 让充电进度有序
        progressEsPO.setId(snowflakeIdGenerator.nextId()+"");
        //2.写入es
        processEsRepository.save(progressEsPO);
    }

    private ProgressCostVO calculateCost(ProgressMessage msg) {
        //1.组织一个调用param参数 所有属性都来自于设备的消息
        ProgressCostParam param=new ProgressCostParam();
        BeanUtils.copyProperties(msg,param);
        //2.直接调用costClient 解析JsonResult
        JsonResult<ProgressCostVO> result = costClient.calculateCost(param);
        //3.解析
        if (result!=null&&result.getCode()==0){
            //当前结果才是正确的
            return result.getData();
        }else{
            log.error("调用计费中心失败,失败原因:{}",result.getMessage());
            throw new RuntimeException("调用计费中心失败");
        }
    }

    private void checkTemperature(Double temperature) {
        if (temperature<10000d){
            log.debug("温度安全条件满足");
        }else{
            log.error("温度安全条件不满足,温度为:{}",temperature);
            throw new RuntimeException("温度安全条件不满足");
        }
    }

    private void saveBillFail(CheckResultMessage msg) {
        //1.组织一个ChargingBillFailPO
        ChargingBillFailPO fail=new ChargingBillFailPO();
        //1.1从消息直接获取
        fail.setBillId(msg.getOrderNo());
        fail.setFailDesc(msg.getFailDesc());
        fail.setUserId(msg.getUserId());
        fail.setGunId(msg.getGunId());
        //1.2业务补充的属性或者默认值
        fail.setCreateTime(new Date());
        fail.setUpdateTime(fail.getCreateTime());
        fail.setDeleted(0);
        //2.调用仓储层,执行新增失败订单的方法写入数据库
        billRepository.saveFailBill(fail);
    }

    private void saveBillSuccess(CheckResultMessage msg) {
        //1.组织一个ChargingBillSuccessPO
        ChargingBillSuccessPO success=new ChargingBillSuccessPO();
        //1.1从消息获取补充字段
        success.setBillId(msg.getOrderNo());
        success.setUserId(msg.getUserId());
        success.setGunId(msg.getGunId());
        //当前启动阶段 无法补充属性charging_capacity(充电度数) charging_duration(充电时长) charging_end_time
        //支付完了才知道的属性pay_amount pay_time pay_channel
        //1.2业务补充计算的 bill_status 1正在充电 2充电完成 3充电异常 4欠费
        success.setBillStatus(1);
        success.setChargingStartTime(new Date());
        success.setUpdateTime(success.getChargingStartTime());
        success.setCreateTime(success.getChargingStartTime());
        success.setDeleted(0);
        //2.调用仓储层,执行新增成功订单的方法写入数据库
        billRepository.saveSuccessBill(success);
    }

    @Override
    public void handleCheckNoRes(DelayCheckMessage msg) {
        //1.使用订单编号查询成功订单
        long successCount=billRepository.countSuccess(msg.getOrderNo());
        //判断存在
        if (successCount==0){
            log.debug("订单:{},成功订单不存在",msg.getOrderNo());
            //2.使用订单编号查询失败订单
            long failCount=billRepository.countFail(msg.getOrderNo());
            //判断存在
            if (failCount==0){
                log.debug("订单:{},失败订单不存在,按照自检失败无响应处理",msg.getOrderNo());
                //3.组织失败订单写入数据库
                //3.1组织一个ChargingFailBillPO
                ChargingBillFailPO fail=new ChargingBillFailPO();
                fail.setBillId(msg.getOrderNo());
                fail.setUserId(msg.getUserId());
                fail.setGunId(msg.getGunId());
                fail.setFailDesc("设备自检无响应");
                fail.setCreateTime(new Date());
                fail.setUpdateTime(fail.getCreateTime());
                fail.setDeleted(0);
                billRepository.saveFailBill(fail);
                //4.调用设备服务 修改设备的状态为故障
                deviceClient.updateGunStatus(msg.getGunId());
                //5.通知用户换枪 道歉 送优惠券 组织消息 消息推送
                WebSocketResult<String> userMsg=new WebSocketResult<>();
                userMsg.setState(1);
                userMsg.setMessage("设备无响应");
                userMsg.setData("您的订单超时无响应,请换枪,送您一张优惠券https://09F32HG3290");
                try{
                    wsServerPoint.pushMsg(JSON.toJSONString(userMsg),msg.getUserId());
                }catch (Exception e){
                    log.error("推送消息给用户失败,userId:{},消息:{}",msg.getUserId(),JSON.toJSONString(userMsg),e);
                }
            }
        }
    }
}
